#include "RenderSystem.h"

namespace TUMAR { namespace Render {
	std::vector<TimerCallback*> TimerCallback::instances;
		
	int TimerCallback::AddInstance(TimerCallback* instance)
	{
		instances.push_back(instance);

		return instances.size() - 1;
	}

	void TimerCallback::RemoveInstance(TimerCallback* instance)
	{
		instances[instance->instance] = NULL;
	}

	void TimerCallback::glutTimerCallback(int instance)
	{
		if(instances[instance] == NULL)
			return;
		
		instances[instance]->scheduled--;

		if(instances[instance]->stop == 0)
		{
			instances[instance]->Schedule();
			instances[instance]->Tick();
		}
		else
		{
			instances[instance]->stop--;
		}
	}

	TimerCallback::TimerCallback(void) : instance(-1), stop(0), scheduled(0)
	{
	}

	TimerCallback::~TimerCallback(void)
	{
		RemoveInstance(this);
	}

	void TimerCallback::Schedule()
	{
		this->scheduled++;

		glutTimerFunc(this->interval, &TimerCallback::glutTimerCallback, this->instance);
	}

	void TimerCallback::Stop()
	{
		if(this->scheduled > this->stop)
			this->stop++;
	}

	void TimerCallback::Schedule(int interval)
	{
		if(instance == -1)
		{
			this->instance = AddInstance(this);
		}
		this->interval = interval;

		Schedule();
	}

	void AbstractRenderer::Init(void)
	{
	}

	void AbstractRenderer::Resize(int width, int height)
	{
	}

	void AbstractRenderer::glMultTransposedMatrix(const float* matrix)
	{
		float* transposed = new float[16];
		{
			transposed[0] = matrix[0];
			transposed[1] = matrix[4];
			transposed[2] = matrix[8];
			transposed[3] = matrix[12];
			transposed[4] = matrix[1];
			transposed[5] = matrix[5];
			transposed[6] = matrix[9];
			transposed[7] = matrix[13];
			transposed[8] = matrix[2];
			transposed[9] = matrix[6];
			transposed[10]= matrix[10];
			transposed[11]= matrix[14];
			transposed[12]= matrix[3];
			transposed[13]= matrix[7];
			transposed[14]= matrix[11];
			transposed[15]= matrix[15];

			glMultMatrixf(transposed);
		}
		delete transposed;
	}
	
	GLuint AbstractRenderer::LoadTexture(std::string file)
	{
		return ilutGLLoadImage((wchar_t*)file.c_str());
	}

	RenderSystem* RenderSystem::instance = NULL;

	void RenderSystem::glutRenderCallback(void)
	{
		instance->Render();
	}

	void RenderSystem::glutKeyboardCallback(unsigned char key, int x, int y)
	{
		instance->KeyPressed(key);
	}

	void RenderSystem::glutResizeCallback(int width, int height)
	{
		instance->Resize(width, height);
	}

	RenderSystem::RenderSystem(void)
	{
	}

	RenderSystem::~RenderSystem(void)
	{
	}

	void RenderSystem::Init(int argc, char* argv[])
	{
		if(instance != NULL)
			throw new std::exception("Another RenderSystem is already initialized!");

		instance = this;

		glutInit(&argc, argv);
		glutInitWindowPosition(-1, -1);
		glutInitWindowSize(640, 480);
		glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
		glutCreateWindow("ARApp");
		glutDisplayFunc(glutRenderCallback);
		glutReshapeFunc(glutResizeCallback);
		glutKeyboardFunc(glutKeyboardCallback);

		glClearColor(1.0, 0, 0, 0);

		ilInit();
		iluInit();
		ilutRenderer(ILUT_OPENGL);		
	}

	void RenderSystem::AddRenderer(AbstractRenderer* renderer)
	{
		this->renderer.push_back(renderer);

		renderer->Invalidate.connect(glutPostRedisplay);
	}

	void RenderSystem::StartRenderLoop(void)
	{
		for(std::vector<AbstractRenderer*>::iterator it = this->renderer.begin(); it != this->renderer.end(); ++it)
		{
			(*it)->Init();
		}

		glutMainLoop();
	}

	void RenderSystem::Resize(int width, int height)
	{
		for(std::vector<AbstractRenderer*>::iterator it = this->renderer.begin(); it != this->renderer.end(); ++it)
		{
			(*it)->Resize(width, height);
		}
	}

	void RenderSystem::Render(void)
	{
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		
		for(std::vector<AbstractRenderer*>::iterator it = this->renderer.begin(); it != this->renderer.end(); ++it)
		{
			(*it)->Render();
		}

		glutSwapBuffers();
	}
}}
